import streamlit as st
from datetime import datetime, timedelta
import pywencai
import pandas as pd
import plotly.graph_objects as go
import akshare as ak
from contextlib import contextmanager
# 设置全局显示选项
pd.set_option('display.unicode.ambiguous_as_wide', True)
pd.set_option('display.unicode.east_asian_width', True)
pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns', None)
pd.set_option('display.expand_frame_repr', False)
pd.set_option('display.max_colwidth', 100)
@contextmanager
def st_spinner(text="处理中..."):
    try:
        with st.spinner(text):
            yield
    finally:
        pass
def get_trading_days():
    end_date = datetime.now()
    dates = [(end_date - timedelta(days=x)).strftime('%Y%m%d') for x in range(20)]
    min_date_dt = datetime.strptime(min(dates), '%Y%m%d')
    max_date_dt = datetime.strptime(max(dates), '%Y%m%d')
    trade_date_df = ak.tool_trade_date_hist_sina()
    trade_date_df['date'] = pd.to_datetime(trade_date_df['trade_date'])
    mask = (trade_date_df['date'] >= min_date_dt) & (trade_date_df['date'] <= max_date_dt)
    return [date.strftime('%Y%m%d') for date in trade_date_df[mask]['date']]
@st.cache_data(ttl=60 * 60 )
def fetch_stock_data(date):
    query = f"非ST，{date}连续涨停天数排序，涨停原因，涨停封单额"
    try:
        data = pywencai.get(query=query)
        if data.empty or f'涨停封单额[{date}]' not in data.columns:
            return None
        max_days = data[f'连续涨停天数[{date}]'].max()
        highest_stocks = data[data[f'连续涨停天数[{date}]'] == max_days]
        return highest_stocks.sort_values(f'涨停封单额[{date}]', ascending=False)
    except Exception as e:
        st.error(f"查询 {date} 数据时出错: {e}")
        return None
def create_chart(df):
    fig = go.Figure()
    fig.add_trace(go.Scatter(
        x=df['日期'],
        y=df['连续涨停天数'],
        mode='lines+markers+text',
        line=dict(color='#FF6B6B', width=3),
        marker=dict(
            size=14,
            color=df['封板金额(万)'],
            colorscale='Plasma',
            showscale=True,
            colorbar=dict(title='封板金额(万)', x=1.05)
        ),
        text=df['股票简称'],
        textposition='top center',
        textfont=dict(size=10),
        hovertext=[
            f"<b>{row['股票简称']}</b><br>"
            f"日期：{row['日期'].strftime('%m%d')}<br>"  
            f"代码：{row['股票代码']}<br>"
            f"连板：{row['连续涨停天数']}天<br>"
            f"封单：{row['封板金额(万)']}万<br>"
            f"原因：{row['涨停原因']}"
            for _, row in df.iterrows()
        ],
        hoverinfo='text'
    ))
    fig.update_layout(
        title='📈 每日最高连板龙头股分析',
        xaxis_title='',
        yaxis_title='连板天数',
        xaxis=dict(
            tickangle=45,
            type='date',  # 改为时间轴类型
            tickformat="%m%d",  # 设置四位月日格式
            gridcolor='#f0f0f0'
        ),
        yaxis=dict(
            tickmode='linear',
            dtick=1,
            gridcolor='#f0f0f0'
        ),
        height=650,
        margin=dict(l=50, r=30, t=100, b=80),
        hoverlabel=dict(
            bgcolor='white',
            font_size=12,
            font_family="Microsoft YaHei"
        ),
        plot_bgcolor='rgba(255,255,255,0.9)',
        paper_bgcolor='rgba(255,255,255,0.8)'
    )
    return fig
def app():
    st.title('🚀 涨停龙头股分析系统')
    with st_spinner("正在获取最新行情数据..."):
        trading_days = get_trading_days()
        chart_data = []
        table_data = []
        for date in trading_days:
            stocks = fetch_stock_data(date)
            if stocks is not None and not stocks.empty:
                # 图表数据取封单金额最大
                top_stock = stocks.iloc[0]
                chart_data.append({
                    '日期': pd.to_datetime(date),
                    '股票简称': top_stock['股票简称'],
                    '股票代码': top_stock['股票代码'],
                    '连续涨停天数': top_stock[f'连续涨停天数[{date}]'],
                    '涨停原因': top_stock[f'涨停原因类别[{date}]'],
                    '封板金额(万)':  round(float(top_stock[f'涨停封单额[{date}]']) / 10000, 2)
                })
                # 表格数据保存全部
                for _, row in stocks.iterrows():
                    table_data.append({
                        '日期': pd.to_datetime(date),
                        '股票简称': row['股票简称'],
                        '股票代码': row['股票代码'],
                        '连续涨停天数': row[f'连续涨停天数[{date}]'],
                        '涨停原因': row[f'涨停原因类别[{date}]'],
                        '封板金额(万)':  round(float(row[f'涨停封单额[{date}]']) / 10000, 2)
                    })
        if chart_data:
            df_chart = pd.DataFrame(chart_data).sort_values('日期')
            df_table = pd.DataFrame(table_data)
            df_table = df_table.sort_values('日期', ascending=False)
            # 绘制图表
            st.subheader("龙头股趋势分析")
            st.plotly_chart(create_chart(df_chart), use_container_width=True)
            # 展示数据表格
            st.subheader("📊 详细数据")
            for date, group in df_table.groupby('日期', sort=False):
                with st.expander(f"{date.strftime('%Y-%m-%d')} 当日最高连板股（共 {len(group)} 支）"):
                    col1, col2 = st.columns([1, 3])
                    with col1:
                        st.metric("最高连板天数", f"{group.iloc[0]['连续涨停天数']} 天")
                    with col2:
                        st.metric("最大封单金额",
                                  f"{group['封板金额(万)'].max():.2f} 万",
                                  help="当日同高度连板股中最大封单金额")
                    styled_df = group[['股票简称', '股票代码', '连续涨停天数', '封板金额(万)', '涨停原因']] \
                        .sort_values('封板金额(万)', ascending=False) \
                        .style.format({
                        '封板金额(万)': '{:.2f}万',
                        '连续涨停天数': '{:.0f}天'
                    }) \
                        .background_gradient(subset=['封板金额(万)'], cmap='YlGn')
                    st.dataframe(
                        styled_df,
                        hide_index=True,
                        use_container_width=True,
                        column_config={
                            "股票代码": st.column_config.TextColumn(
                                width="medium",
                                help="点击查看股票详情"
                            )
                        }
                    )
        else:
            st.warning("未获取到有效数据，请稍后重试")
if __name__ == "__main__":
    app()